/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.apache.shardingsphere.infra.metadata.database.schema.reviser.schema;


import org.apache.shardingsphere.infra.config.props.ConfigurationProperties;
import org.apache.shardingsphere.infra.database.core.metadata.data.model.SchemaMetaData;
import org.apache.shardingsphere.infra.database.core.metadata.data.model.TableMetaData;
import org.apache.shardingsphere.infra.metadata.database.schema.reviser.MetaDataReviseEntry;
import org.apache.shardingsphere.infra.metadata.database.schema.reviser.table.TableMetaDataReviseEngine;
import org.apache.shardingsphere.infra.rule.ShardingSphereRule;
import org.apache.shardingsphere.infra.spi.type.ordered.OrderedSPILoader;

import java.util.Collection;
import java.util.Comparator;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * Schema meta data revise engine.
 */
//@RequiredArgsConstructor
public final class SchemaMetaDataReviseEngine {

    private final Collection<ShardingSphereRule> rules;

    private final ConfigurationProperties props;

    public SchemaMetaDataReviseEngine(Collection<ShardingSphereRule> rules, ConfigurationProperties props) {
        this.rules = rules;
        this.props = props;
    }

    /**
     * Revise schema meta data.
     *
     * @param originalMetaData original schema meta data
     * @return revised schema meta data
     */
    @SuppressWarnings({"rawtypes", "unchecked"})
    public SchemaMetaData revise(final SchemaMetaData originalMetaData) {
        // 创建修订后的SchemaMetaData对象，初始化为原始元数据
        SchemaMetaData result = originalMetaData;

        // 遍历通过OrderedSPILoader获取的MetaDataReviseEntry服务列表
        for (Entry<ShardingSphereRule, MetaDataReviseEntry> entry : OrderedSPILoader.getServices(MetaDataReviseEntry.class, rules).entrySet()) {
            // 调用revise方法，对SchemaMetaData进行修订
            // entry.getKey() 获取ShardingSphereRule
            // entry.getValue() 获取MetaDataReviseEntry
            result = revise(result, entry.getKey(), entry.getValue());
        }

        // 返回修订后的SchemaMetaData
        return result;
    }


    /**
     * 修改Schema元数据
     *
     * @param <T>              泛型类型，继承自ShardingSphereRule
     * @param originalMetaData 原始Schema元数据
     * @param rule             分片规则
     * @param reviseEntry      元数据修改条目
     * @return 修改后的Schema元数据
     */
    private <T extends ShardingSphereRule> SchemaMetaData revise(final SchemaMetaData originalMetaData, final T rule, final MetaDataReviseEntry<T> reviseEntry) {
        // 创建一个TableMetaDataReviseEngine实例，用于修改表的元数据
        TableMetaDataReviseEngine<T> tableMetaDataReviseEngine = new TableMetaDataReviseEngine<>(rule, reviseEntry);

        // 获取SchemaTableAggregationReviser实例，用于修改聚合的Schema和Table元数据
        Optional<? extends SchemaTableAggregationReviser<T>> aggregationReviser = reviseEntry.getSchemaTableAggregationReviser(props);

        // 如果aggregationReviser不存在，则直接返回修改后的SchemaMetaData
        if (!aggregationReviser.isPresent()) {
            // 遍历原始SchemaMetaData中的每个TableMetaData，并使用tableMetaDataReviseEngine进行修改
            return new SchemaMetaData(
                    originalMetaData.getName(),
                    originalMetaData.getTables().stream()
                            .map(tableMetaDataReviseEngine::revise)
                            .collect(Collectors.toList())
            );
        }

        int i = 0;
        System.out.println("revise table:"+rule);
        // 遍历原始SchemaMetaData中的每个TableMetaData
        for (TableMetaData each : originalMetaData.getTables().stream().sorted(Comparator.comparing(TableMetaData::getName)).toList()) {
            // 使用aggregationReviser对TableMetaData进行修改
            TableMetaData revise = tableMetaDataReviseEngine.revise(each);
            String oldName = each.getName();
            String newName = revise.getName();
            System.out.println(i + ":revise table:" + oldName + " to " + newName);
            i++;
            aggregationReviser.get().add(revise);
        }

        // 返回聚合后的SchemaMetaData
        return new SchemaMetaData(
                originalMetaData.getName(),
                aggregationReviser.get().aggregate(rule)
        );
    }

}
